<?php
namespace App\Utils;

use App\Models\MUser;
use DB;
use Illuminate\Database\Eloquent\Collection;

/**
 * 树形结构帮助类
 */
class TreeUtil
{
    /**
     * 根据节点人生成最近节点链条
     * @param int $nid 节点人ID
     * @return string 链条
     */
    public static function getNListById($nid)
    {
        $nUser = MUser::find($nid);
        return self::getNListByUser($nUser);
    }

    /**
     * 根据节点人生成最近节点链条
     * @param string $nName 节点人用户名
     * @return string 链条
     */
    public static function getNListByName($nName)
    {
        $nUser = MUser::where('username', $nName)->first();
        return self::getNListByUser($nUser);
    }

    /**
     * 根据节点人生成最近节点链条
     * @param MUser $nUser 节点人对象
     * @return string 链条
     */
    public static function getNListByUser($nUser)
    {
        $result = "";
        preg_match_all("/\[(\w+)\]/", $nUser->nlist, $matchs);
        if (count($matchs[1]) >= 100) {  //如果元素已经超过100层,则从父节点开始进行拼接
            $result .= "[$nUser->id]";
        } else {
            $result .= $nUser->nlist . "[$nUser->id]";
        }
        return $result;
    }


    /**
     * 根据推荐人生成最近节点链条
     * @param int $pid 推荐人ID
     * @return string 链条
     */
    public static function getPListById($pid)
    {
        $pUser = MUser::find($pid);
        return self::getPListByUser($pUser);
    }

    /**
     * 根据推荐人生成最近节点链条
     * @param string $pName 推荐人用户名
     * @return string 链条
     */
    public static function getPListByName($pName)
    {
        $pUser = MUser::where('username', $pName)->first();
        return self::getPListByUser($pUser);
    }

    /**
     * 根据推荐人生成最近节点链条
     * @param MUser $pUser 推荐人对象
     * @return string 链条
     */
    public static function getPListByUser($pUser)
    {
        $result = "";
        preg_match_all("/\[(\w+)\]/", $pUser->plist, $matchs);
        if (count($matchs[1]) >= 100) {  //如果元素已经超过100层,则从父节点开始进行拼接
            $result .= "[$pUser->id]";
        } else {
            $result .= $pUser->plist . "[$pUser->id]";
        }
        return $result;
    }

    /**
     * 检查报单中心是否有效
     * @param int $uid 报单中心ID
     * @return MUser 报单中心对象 or Null
     */
    public static function checkBdzxById($uid)
    {
        $bdzx = MUser::find($uid);
        return self::checkBdzxByUser($bdzx);
    }

    /**
     * 检查报单中心是否有效
     * @param string $username 报单中心用户名
     * @return MUser 报单中心对象 or Null
     */
    public static function checkBdzxByName($username)
    {
        $bdzx = MUser::where('username', $username)->first();
        return self::checkBdzxByUser($bdzx);
    }

    /**
     * 检查报单中心是否有效
     * @param MUser $user 报单中心对象
     * @return MUser 报单中心对象 or Null
     */
    public static function checkBdzxByUser($user)
    {
        $result = null;
        if (empty($user)) {
            return null;
        }

        if ($user->ok_status == MUser::$STATUS_ACTIVATED
            && $user->bdzx_status == MUser::$BDZX_YES
        ) {
            $result = $user;
        }

        return $result;
    }

    /**
     * 检查报推荐人是否有效
     * @param int $uid 推荐人ID
     * @return MUser 推荐人对象 or Null
     */
    public static function checkPUserById($uid)
    {
        $pUser = MUser::find($uid);
        return self::checkPUserByUser($pUser);
    }

    /**
     * 检查报推荐人是否有效
     * @param string $username 推荐人用户名
     * @return MUser 推荐人对象 or Null
     */
    public static function checkPUserByName($username)
    {
        $pUser = MUser::where('username', $username)->first();
        return self::checkPUserByUser($pUser);
    }

    /**
     * 检查节点人是否有效
     * @param int $uid 节点人ID
     * @return MUser 节点人对象 or Null
     */
    public static function checkNUserById($uid)
    {
        $nUser = MUser::find($uid);
        return self::checkNUserByUser($nUser);
    }

    /**
     * 检查节点人是否有效
     * @param string $username 节点人用户名
     * @return MUser 节点人对象 or Null
     */
    public static function checkNUserByName($username)
    {
        $nUser = MUser::where('username', $username)->first();
        return self::checkNUserByUser($nUser);
    }

    /**
     * 检查报推荐人是否有效
     * @param MUser $user 推荐人对象
     * @return MUser 推荐人对象 or Null
     */
    public static function checkPUserByUser($user)
    {
        $result = null;

        if (empty($user)) {
            return null;
        }

        if ($user->ok_status == MUser::$STATUS_ACTIVATED) {
            $result = $user;
        }

        return $result;
    }

    /**
     * 检查节点人是否有效
     * @param MUser $user 节点人对象
     * @return MUser 节点人对象 or Null
     */
    public static function checkNUserByUser($user)
    {
        $result = null;

        if (empty($user)) {
            return null;
        }

        if ($user->ok_status == MUser::$STATUS_ACTIVATED) {
            $result = $user;
        }

        return $result;
    }

    /**
     * 获取指定接点人的安插位置
     * @param int $uid 指定接点人ID
     * @return int 位置
     */
    public static function getNodeAddrById($uid)
    {
        $nUser = MUser::find($uid);
        return self::getNodeAddrByUser($nUser);
    }

    /**
     * 获取指定接点人的安插位置
     * @param string $username 指定接点人ID
     * @return int 位置
     */
    public static function getNodeAddrByName($username)
    {
        $nUser = MUser::where('username', $username)->first();
        return self::getNodeAddrByUser($nUser);
    }

    /**
     * 获取指定接点人的安插位置
     * @param MUser $user 指定接点人ID
     * @return int 位置
     */
    public static function getNodeAddrByUser($user)
    {
        $result = MUser::$ADDR_NO;

        if (!empty($user)) {

            //生成子节点AB区位置链条
            $childs = MUser::where('nid', $user->id)->get();
            $chain = "->";
            foreach ($childs as $c) {
                if ($c->addr == MUser::$ADDR_A) {
                    $chain .= "A";
                } elseif ($c->addr == MUser::$ADDR_B) {
                    $chain .= "B";
                }
            }

            if (!strpos($chain, "A")) {
                $result = MUser::$ADDR_A;
            } elseif (!strpos($chain, "B")) {
                $result = MUser::$ADDR_B;
            } else {
                $result = MUser::$ADDR_NO;
            }

        }

        return $result;
    }

    /**
     * 获取当前节点树的根节点(节点树)
     *
     * @return MUser 根节点用户对象
     */
    public static function getNTreeRoot()
    {
        $root = MUser::where('nid', 0)->first();

        return $root;
    }

    /**
     * 获取当前推荐树的根节点(推荐树)
     *
     * @return MUser 根节点用户对象
     */
    public static function getPTreeRoot()
    {
        $root = MUser::where('pid', 0)->first();

        return $root;
    }

    /**
     * 判断 uid1 是否是 uid2 的子会员（节点树）
     * @param int $uid1
     * @param int $uid2
     * @return bool
     */
    public static function hasNChildById($uid1, $uid2)
    {
        $u1 = MUser::find($uid1);
        $u2 = MUser::find($uid2);

        return self::hasNChildByUser($u1, $u2);
    }

    /**
     * 判断 name1 是否是 name2 的子会员 (节点树)
     *
     * @param string $name1
     * @param string $name1
     *
     * @return bool
     */
    public static function hasNChildByName($name1, $name2)
    {
        $u1 = MUser::where('username', $name1)->first();
        $u2 = MUser::where('username', $name2)->first();

        return self::hasNChildByUser($u1, $u2);
    }

    /**
     * 判断 user1 是否是 user2 的子会员（节点树）
     *
     * @param MUser $user1
     * @param MUser $user2
     *
     * @return bool
     */
    public static function hasNChildByUser($user1, $user2)
    {
        if (empty($user1) || empty($user2)) {
            return false;
        }

        //得到 user1 的完整父节点链条，并进行父子关系判断
        $chain = self::getNChainByUser($user1);
        if (strpos($chain, "[$user2->id]") === false) {
            return false;
        }

        return true;
    }

    /**
     * 判断 uid1 是否是 uid2 的子会员（推荐树）
     * @param int $uid1
     * @param int $uid2
     * @return bool
     */
    public static function hasPChildById($uid1, $uid2)
    {
        $u1 = MUser::find($uid1);
        $u2 = MUser::find($uid2);

        return self::hasPChildByUser($u1, $u2);
    }

    /**
     * 判断 name1 是否是 name2 的子会员 (推荐树)
     *
     * @param string $name1
     * @param string $name1
     *
     * @return bool
     */
    public static function hasPChildByName($name1, $name2)
    {
        $u1 = MUser::where('username', $name1)->first();
        $u2 = MUser::where('username', $name2)->first();

        return self::hasPChildByUser($u1, $u2);
    }

    /**
     * 判断 user1 是否是 user2 的子会员（推荐树）
     *
     * @param MUser $user1
     * @param MUser $user2
     *
     * @return bool
     */
    public static function hasPChildByUser($user1, $user2)
    {
        if (empty($user1) || empty($user2)) {
            return false;
        }

        //得到 user1 的完整父节点链条，并进行父子关系判断
        $chain = self::getPChainByUser($user1);
        if (strpos($chain, "[$user2->id]") === false) {
            return false;
        }

        return true;
    }

    /**
     * 得到某会员的完整父节点（节点树）链条
     *
     * @param int $uid 会员ID
     *
     * @return string 链条
     */
    public static function getNChainById($uid)
    {
        $user = MUser::find($uid);
        return self::getNChainByUser($user);
    }

    /**
     * 得到某会员的完整父节点（节点树）链条
     *
     * @param string $name 会员名称
     *
     * @return string 链条
     */
    public static function getNChainByName($name)
    {
        $user = MUser::where('username', $name)->first();

        return self::getNChainByUser($user);
    }

    /**
     * 得到某会员的完整父节点（节点树）链条
     *
     * @param MUser $user 会员对象
     *
     * @return string 链条
     */
    public static function getNChainByUser($user)
    {
        $result = "";
        $temp = $user->nlist;
        while (!empty($temp)) {
            $result .= $temp;
            //取最靠前的用户的节点链条继续进行拼接
            preg_match_all("/\[(\w+)\]/", $temp, $matchs);
            $nuser = MUser::select(['nlist'])->where('id', $matchs[1][0])->first();
            if (!empty($nuser)) {
                $temp = $nuser->nlist;
            } else {
                $temp = "";
            }

        }

        return $result;
    }

    /**
     * 得到某会员的完整父节点（推荐树）链条
     *
     * @param int $uid 会员ID
     *
     * @return string 链条
     */
    public static function getPChainById($uid)
    {
        $user = MUser::find($uid);
        return self::getPChainByUser($user);
    }

    /**
     * 得到某会员的完整父节点（推荐树）链条
     *
     * @param string $name 会员名称
     *
     * @return string 链条
     */
    public static function getPChainByName($name)
    {
        $user = MUser::where('username', $name)->first();

        return self::getPChainByUser($user);
    }

    /**
     * 得到某会员的完整父节点（节点树）链条
     *
     * @param MUser $user 会员对象
     *
     * @return string 链条
     */
    public static function getPChainByUser($user)
    {
        $result = "";

        $temp = $user->plist;
        while (!empty($temp)) {
            $result .= $temp;

            //取最靠前的用户的节点链条继续进行拼接
            preg_match_all("/\[(\w+)\]/", $temp, $matchs);
            $nuser = MUser::select(['plist'])->where('id', $matchs[1][0])->first();
            if (!empty($nuser)) {
                $temp = $nuser->plist;
            } else {
                $temp = "";
            }

        }

        return $result;
    }

    /**
     * 获取所有的子节点会员（节点树）
     *
     * @param int $uid 会员ID
     *
     * @return array 子会员ID数组
     */
    public static function getNChildsById($uid)
    {
        $user = MUser::find($uid);
        return self::getNChildsByUser($user);
    }

    /**
     * 获取所有的子节点会员（节点树）
     *
     * @param string $name 会员用户名
     *
     * @return array 子会员ID数组
     */
    public static function getNChildsByName($name)
    {
        $user = MUser::where('username', $name)->first();
        return self::getNChildsByUser($user);
    }

    /**
     * 获取所有的子节点会员（节点树）
     *
     * @param MUser $user 会员对象
     *
     * @return array 子会员ID数组
     */
    public static function getNChildsByUser($user)
    {
        $result = array();
        if (empty($user)) {
            return $result;
        }

        $batch = MUser::select(['id', 'nlist', 'ntree'])
            ->where('nlist', 'like', "%[$user->id]%")->get();

        self::recursionNChild($batch, $result);

        $result = array_unique($result, SORT_NUMERIC);    //子会员ID集合去重复
        return $result;
    }

    private static function recursionNChild(Collection $batch, &$result)
    {
        if (empty($batch)) {
            return;
        }

        //遍历每一个用户，并把用户ID加入到集合当中
        foreach ($batch as $chain) {
            $result[] = $chain->id;
        }

        //对深度最大的一批会员进行递归遍历
        $maxUser = $batch->where('ntree', $batch->max('ntree'));
        foreach ($maxUser as $user) {
            $newBatch = MUser::select(['id', 'nlist', 'ntree'])
                ->where('nlist', 'like', "%[$user->id]%")->get();

            self::recursionNChild($newBatch, $result);
        }
    }

    /**
     * 获取所有的子节点会员（推荐树）
     *
     * @param int $uid 会员ID
     *
     * @return array 子会员ID数组
     */
    public static function getPChildsById($uid)
    {
        $user = MUser::find($uid);
        return self::getPChildsByUser($user);
    }

    /**
     * 获取所有的子节点会员（推荐树）
     *
     * @param string $name 会员用户名
     *
     * @return array 子会员ID数组
     */
    public static function getPChildsByName($name)
    {
        $user = MUser::where('username', $name)->first();
        return self::getPChildsByUser($user);
    }

    /**
     * 获取所有的子节点会员（推荐树）
     *
     * @param MUser $user 会员对象
     *
     * @return array 子会员ID数组
     */
    public static function getPChildsByUser($user)
    {
        $result = array();
        if (empty($user)) {
            return $result;
        }

        $batch = MUser::select(['id', 'plist', 'ptree'])
            ->where('plist', 'like', "%[$user->id]%")->get();

        self::recursionPChild($batch, $result);

        $result = array_unique($result, SORT_NUMERIC);    //子会员ID集合去重复
        return $result;
    }

    /**
     * 递归方法查找子会员
     * @param Collection $batch
     * @param $result
     */
    private static function recursionPChild(Collection $batch, &$result)
    {
        if (empty($batch)) {
            return;
        }

        //遍历每一个用户，并把用户ID加入到集合当中
        foreach ($batch as $chain) {
            $result[] = $chain->id;
        }

        //对深度最大的一批会员进行递归遍历
        $maxUser = $batch->where('ptree', $batch->max('ptree'));
        foreach ($maxUser as $user) {
            $newBatch = MUser::select(['id', 'plist', 'ptree'])
                ->where('plist', 'like', "%[$user->id]%")->get();

            self::recursionPChild($newBatch, $result);
        }
    }

    /**
     * 获取所有的父节点会员（节点树）
     * @param int $uid 用户ID
     * @return array 父节点ID数组
     */
    public static function getNParentsById($uid)
    {
        $user = MUser::find($uid);
        return self::getNParentsByUser($user);
    }

    /**
     * 获取所有的父节点会员（节点树）
     * @param string $name 用户账号
     * @return array 父节点ID数组
     */
    public static function getNParentsByName($name)
    {
        $user = MUser::where('username', $name)->first();
        return self::getNParentsByUser($user);
    }

    /**
     * 获取所有的父节点会员（节点树）
     * @param MUser $user 用户对象
     * @return array 父节点ID数组
     */
    public static function getNParentsByUser($user)
    {
        $chain = self::getNChainByUser($user);
        preg_match_all("/\[(\w+)\]/", $chain, $matchs);
        return $matchs[1];
    }

    /**
     * 获取所有父节点会员（推荐树）
     * @param int $id 用户ID
     * @return array 父节点ID数组
     */
    public static function getPParentsById($id)
    {
        $user = MUser::find($id);
        return self::getPParentsByUser($user);
    }

    /**
     * 获取所有父节点会员（推荐树）
     * @param string $name 用户账号
     * @return array 父节点ID数组
     */
    public static function getPParentsByName($name)
    {
        $user = MUser::where('username', $name)->first();
        return self::getPParentsByUser($user);
    }

    /**
     * 获取所有父节点会员（推荐树）
     * @param MUser $user 用户对象
     * @return array 父节点ID数组
     */
    public static function getPParentsByUser($user)
    {
        $chain = self::getPChainByUser($user);
        preg_match_all("/\[(\w+)\]/", $chain, $matchs);
        return $matchs[1];
    }

    /**
     * 获取指定层级以内的父节点会员（节点树）
     * @param int $id 用户ID
     * @param int $num 限制层级
     * @return array 父节点ID数组
     */
    public static function getNParentsNumById($id, $num)
    {
        $user = MUser::find($id);
        return self::getNParentsNumByUser($user, $num);
    }

    /**
     * 获取指定层级以内的父节点会员（节点树）
     * @param string $name 用户账号
     * @param int $num 限制层级
     * @return array 父节点ID数组
     */
    public static function getNParentsNumByName($name, $num)
    {
        $user = MUser::where('username', $name)->first();
        return self::getNParentsNumByUser($user, $num);
    }

    /**
     * 获取指定层级的父节点会员（节点树）
     * @param MUser $user 用户对象
     * @param int $num 限制层级
     * @return array 父节点ID数组
     */
    public static function getNParentsNumByUser($user, $num)
    {
        $allId = self::getNParentsByUser($user);
        $result = array_slice($allId, 0, $num); //裁剪指定深度内的数组
        return $result;
    }

    /**
     * 获取指定层级以内的父节点会员（推荐树）
     * @param int $id 用户ID
     * @param int $num 限制层级
     * @return array 父节点ID数组
     */
    public static function getPParentsNumById($id, $num)
    {
        $user = MUser::find($id);
        return self::getPParentsNumByUser($user, $num);
    }

    /**
     * 获取指定层级以内的父节点会员（推荐树）
     * @param string $name 用户账号
     * @param int $num 限制层级
     * @return array 父节点ID数组
     */
    public static function getPParentsNumByName($name, $num)
    {
        $user = MUser::where('username', $name)->first();
        return self::getPParentsNumByUser($user, $num);
    }

    /**
     * 获取指定层级以内的父节点会员（推荐树）
     * @param MUser $user
     * @param int $num
     * @return array 父节点ID数组
     */
    public static function getPParentsNumByUser($user, $num)
    {
        $allId = self::getPParentsByUser($user);
        $result = array_slice($allId, 0, $num); //裁剪指定深度内的数组
        return $result;
    }

    /**
     * 从（节点树）获取一个可以插入的节点用户对象（随机）
     *
     * @return MUser 节点用户对象 or null
     */
    public static function getNCanNode()
    {
        /**
         * 从数据库查找可插入位置的节点
         * SELECT * FROM (
         * SELECT id, username, (SELECT COUNT(*) FROM users AS temp where temp.nid=users.id) AS child FROM users
         * ) as R WHERE R.child=0 or R.child=1 ORDER BY R.child DESC LIMIT 1;
         */
        $result = DB::select("SELECT * FROM (
	        SELECT id, username, (SELECT COUNT(*) FROM users AS temp where temp.nid=users.id) AS child FROM users
        ) as R WHERE R.child=0 or R.child=1 ORDER BY R.child DESC LIMIT 1");

        return MUser::find($result[0]->id);
    }

    /**
     * 获取报单中心的子会员
     * @param int $uid 报单中心人ID
     * @param bool $all 是否包括未激活会员
     * @return Collection 直接子会员集合
     */
    public static function getBdUsersById($uid, $all = false)
    {
        $bdzx = MUser::find($uid);
        return self::getBdUsersByUser($bdzx, $all);
    }

    /**
     * 获取报单中心的子会员
     * @param string $username 报单中心用户名
     * @param bool $all 是否包括未激活会员
     * @return Collection 直接子会员集合
     */
    public static function getBdUsersByName($username, $all = false)
    {
        $bdzx = MUser::where('username', $username)->first();
        return self::getBdUsersByUser($bdzx, $all);
    }

    /**
     * 获取报单中心的子会员
     * @param MUser $bdzx 报单中心人
     * @param bool $all 是否包括未激活会员
     * @return Collection 直接子会员集合
     */
    public static function getBdUsersByUser($bdzx, $all = false)
    {
        $query = (new MUser())->query();
        $query = $query->where('bdzx', $bdzx->id);
        if (!$all) {
            $query = $query->where('ok_status', MUser::$STATUS_ACTIVATED);
        }
        $allUser = $query->get();
        return $allUser;
    }
}